/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    IsrDpc.c

Abstract:

    Contains routines related to interrupt and dpc handling.

Environment:

    Kernel mode

--*/

#include "driver.h"
#include "device.h"
#include "IsrDpc.h"

/*++
Routine Description:

Configure and create the WDFINTERRUPT object.
This routine is called by EvtDeviceAdd callback.

Arguments:

pDevExt      Pointer to our DEVICE_EXTENSION

Return Value:

NTSTATUS code

--*/
NTSTATUS
PCIEInterruptCreate(IN PDEVICE_EXTENSION pDevExt)
{
    NTSTATUS                    Status;
    WDF_INTERRUPT_CONFIG        InterruptConfig;

    WDF_INTERRUPT_CONFIG_INIT( &InterruptConfig,
                               PCIEEvtInterruptIsr,
                               PCIEEvtInterruptDpc );

    InterruptConfig.EvtInterruptEnable  = PCIEEvtInterruptEnable;
    InterruptConfig.EvtInterruptDisable = PCIEEvtInterruptDisable;

    InterruptConfig.AutomaticSerialization = TRUE;

    //
    // Unlike WDM, framework driver should create interrupt object in EvtDeviceAdd and
    // let the framework do the resource parsing and registration of ISR with the kernel.
    // Framework connects the interrupt after invoking the EvtDeviceD0Entry callback
    // and disconnect before invoking EvtDeviceD0Exit. EvtInterruptEnable is called after
    // the interrupt interrupt is connected and EvtInterruptDisable before the interrupt is
    // disconnected.
    //
    Status = WdfInterruptCreate( 
                pDevExt->Device, 
                &InterruptConfig, 
                WDF_NO_OBJECT_ATTRIBUTES, 
                &pDevExt->Interrupt );

    if( !NT_SUCCESS(Status) )
    {
        KdPrint(("PCIEInterruptCreate: Failed: %x", Status));
    }
    
    return Status;
}

/*++
Routine Description:

Interrupt handler for this driver. Called at DIRQL level when the
device or another device sharing the same interrupt line asserts
the interrupt. The driver first checks the device to make sure whether
this interrupt is generated by its device and if so clear the interrupt
register to disable further generation of interrupts and queue a
DPC to do other I/O work related to interrupt - such as reading
the device memory, starting a DMA transaction, coping it to
the request buffer and completing the request, etc.

Arguments:

Interupt   - Handle to WDFINTERRUPT Object for this device.
MessageID  - MSI message ID (always 0 in this configuration)

Return Value:

TRUE   --  This device generated the interrupt.
FALSE  --  This device did not generated this interrupt.

--*/
BOOLEAN PCIEEvtInterruptIsr(IN WDFINTERRUPT Interrupt, IN ULONG MessageID)
{
    PDEVICE_EXTENSION   pDevExt;
    BOOLEAN             IsRecognized = FALSE;

    UNREFERENCED_PARAMETER(MessageID);

    // Our ISR Routine will be called event if the test card hasn't trigger any interrupt signal.
    pDevExt  = PCIEGetDeviceContext(WdfInterruptGetDevice(Interrupt));

    
    if (IsRecognized)
    {
        WdfInterruptQueueDpcForIsr(pDevExt->Interrupt);
    }

    return IsRecognized;
}

/*++

Routine Description:

    DPC callback for ISR. Please note that on a multiprocessor system,
    you could have more than one DPCs running simulataneously on
    multiple processors. So if you are accesing any global resources
    make sure to synchrnonize the accesses with a spinlock.

Arguments:

    Interupt  - Handle to WDFINTERRUPT Object for this device.
    Device    - WDFDEVICE object passed to InterruptCreate

Return Value:

--*/
VOID PCIEEvtInterruptDpc(IN WDFINTERRUPT Interrupt,IN WDFDEVICE Device)
{
    NTSTATUS            Status;
    PDEVICE_EXTENSION   pDevExt;
    BOOLEAN             fTxInterrupt = FALSE;
    BOOLEAN             fRxInterrupt = FALSE;
    
    UNREFERENCED_PARAMETER(Device);

    pDevExt  = PCIEGetDeviceContext(WdfInterruptGetDevice(Interrupt));
    
    do
    {
        //
        // Acquire this device's InterruptSpinLock.
        //
        WdfInterruptAcquireLock( Interrupt );

        WdfInterruptReleaseLock( Interrupt );

      
    }while (FALSE);
    
    return;
}

/*++

Routine Description:

    Called by the framework at DIRQL immediately after registering the ISR with the kernel
    by calling IoConnectInterrupt.

Return Value:

    NTSTATUS
--*/
NTSTATUS PCIEEvtInterruptEnable(
            IN WDFINTERRUPT Interrupt,
            IN WDFDEVICE    Device)
{
    UNREFERENCED_PARAMETER(Interrupt);
    UNREFERENCED_PARAMETER(Device);

    KdPrint(("PCIEEvtInterruptEnable"));

    return STATUS_SUCCESS;
}

NTSTATUS
PCIEEvtInterruptDisable(
    IN WDFINTERRUPT Interrupt,
    IN WDFDEVICE    Device
    )
/*++

Routine Description:

    Called by the framework at DIRQL before Deregistering the ISR with the kernel
    by calling IoDisconnectInterrupt.

Return Value:

    NTSTATUS
--*/
{
    UNREFERENCED_PARAMETER(Interrupt);
    UNREFERENCED_PARAMETER(Device);

    KdPrint(("PCIEEvtInterruptDisable"));

    return STATUS_SUCCESS;
}